import asyncio
import logging
from struct import pack, unpack
from time import time
from typing import List, Tuple, Type

import numpy as np

from pynq import Interrupt, Overlay, allocate, buffer
from pynq.overlay import DefaultIP
from xlnk import Xlnk

from command import *
from config import config
from defines import *
from tcp_result_server import TCP_RESULT_SERVER
from worker import HANDLERS, WORKER

from socket import socket

import struct 
import math

import os_ctrl
# from fit import fit_frame
# from fit_lib import lib
# from cffi import FFI


logger = logging.getLogger()

SPEC_LENGTH_BASE =  1*4
START_SPEC_BASE =   2*4
IND_INTERVAL_BASE = 3*4
BYPASS_LOG_BASE =   4*4         
CH_SEL_BASE =   5*4              
THRESHOLD_OFFSET_BASE = 6*4  
BULK_NUM_BASE = 7*4
START_SAMPLE_BASE = 8*4    
RES_BASE_ADDR_0_BASE =  9*4  
RES_BASE_ADDR_1_BASE =  10*4   
SINGLE_SPEC_CH_BASE =   11*4
RECENT_DONE_BASE = 12*4
PINGPONG_SEL_BASE = 13*4
SINGLE_SPEC_DONE_BASE = 14*4
SINGLE_SPEC_DATA_BASE = 15*4


def float2int_present(num) -> int:
    packed = struct.pack('!f', float(num))
    integers = struct.unpack('!I', packed)[0]
    return integers


class SingleSpecCtrlDriver(DefaultIP):

    bindto = ['xilinx.com:user:single_spec_ctrl:1.0']


    def __init__(self, description) -> None:
        super().__init__(description)

    
    @property
    def single_spec_ch(self) -> int:
        return self.read(SINGLE_SPEC_CH_BASE)
    @single_spec_ch.setter
    def single_spec_ch(self, val):
        self.write(SINGLE_SPEC_CH_BASE, val)


    @property
    def single_spec_done(self) -> int:
        return self.read(SINGLE_SPEC_DONE_BASE)
    @single_spec_done.setter
    def single_spec_done(self, val):
        self.write(SINGLE_SPEC_DONE_BASE, val)


    @property
    def single_spec_data(self) -> int:
        return self.read(SINGLE_SPEC_DATA_BASE)





class SysCtrlDriver(DefaultIP):

    bindto = ['xilinx.com:user:sysctrl_zu3:1.1']


    def __init__(self, description) -> None:
        super().__init__(description)


    @property
    def enable(self) -> bool:
        return (self.read(START_SAMPLE_BASE) != 0)
    @enable.setter
    def enable(self, val):
        val_to_wr = 1 if(val) else 0
        self.write(START_SAMPLE_BASE, val_to_wr)


    @property
    def spec_length(self) -> int:
        return self.read(SPEC_LENGTH_BASE)
    @spec_length.setter
    def spec_length(self, val):
        self.write(SPEC_LENGTH_BASE, val)


    @property
    def start_spec(self) -> int:
        return self.read(START_SPEC_BASE)
    @start_spec.setter
    def start_spec(self, val):
        self.write(START_SPEC_BASE, val)

        
    @property
    def res0_offset(self) -> int:
        return self.read(RES_BASE_ADDR_0_BASE)
    @res0_offset.setter
    def res0_offset(self, val):
        self.write(RES_BASE_ADDR_0_BASE, val)

    
    @property
    def res1_offset(self) -> int:
        return self.read(RES_BASE_ADDR_1_BASE)
    @res1_offset.setter
    def res1_offset(self, val):
        self.write(RES_BASE_ADDR_1_BASE, val)


    @property
    def threshold_offset(self) -> int:
        return self.read(THRESHOLD_OFFSET_BASE)
    @threshold_offset.setter
    def threshold_offset(self, val):
        self.write(THRESHOLD_OFFSET_BASE, val)


    @property
    def bulk_num(self) -> int:
        return self.read(BULK_NUM_BASE)
    @bulk_num.setter
    def bulk_num(self, val):
        self.write(BULK_NUM_BASE, val)
    

    @property
    def ind_interval(self) -> int:
        return self.read(IND_INTERVAL_BASE)
    @ind_interval.setter
    def ind_interval(self, val):
        self.write(IND_INTERVAL_BASE, val)


    @property
    def bypass_log(self) -> bool:                           # bit 0 in BYPASS_LOG_BASE
        return (self.read(BYPASS_LOG_BASE) & 0x01)
    @bypass_log.setter
    def bypass_log(self, val):
        old_val = self.read(BYPASS_LOG_BASE)
        val_to_wr = (old_val | 0x01) if(val) else (old_val & ~0x01)
        self.write(BYPASS_LOG_BASE, val_to_wr)


    @property
    def use_fake_trig(self) -> bool:                        # bit 1 in BYPASS_LOG_BASE
        return ((self.read(BYPASS_LOG_BASE) >> 1) & 0x01)
    @use_fake_trig.setter
    def use_fake_trig(self, val):
        old_val = self.read(BYPASS_LOG_BASE)
        val_to_wr = (old_val | 0x02) if(val) else (old_val & ~0x02)
        self.write(BYPASS_LOG_BASE, val_to_wr)


    @property
    def bypass_fir(self) -> bool:                          # bit 2 in BYPASS_LOG_BASE
        return ((self.read(BYPASS_LOG_BASE) >> 2) & 0x01)
    @bypass_fir.setter
    def bypass_fir(self, val):
        old_val = self.read(BYPASS_LOG_BASE)
        val_to_wr = (old_val | 0x04) if(val) else (old_val & ~0x04)
        self.write(BYPASS_LOG_BASE, val_to_wr)


    @property
    def ch_sel(self) -> CHANEL_SEL:
        return CHANEL_SEL(self.read(CH_SEL_BASE))
    @ch_sel.setter
    def ch_sel(self, val):
        self.write(CH_SEL_BASE, int(val))



    @property
    def pingpong_sel(self) -> int:
        return (self.read(PINGPONG_SEL_BASE) & 0x01)

    @property
    def env_temp(self) -> int:
        temp_bin = self.read(PINGPONG_SEL_BASE) >> 1
        logger.debug(f"temp_bin:{temp_bin}")
        temp0 = (struct.unpack('>i', temp_bin.to_bytes(4, 'big'))[0] * 0.0625)
        temp_int = round(temp0)
        return temp_int



    # @property
    # def optictal_switch(self) -> Tuple[bool, int]:
    #     raw = self.read(OPTICAL_SWITCH_REG)
    #     return bool(raw >> 31), (raw & 0xF)
    # @optictal_switch.setter
    # def optictal_switch(self, val:Tuple[bool, int]):
    #     val_to_wr = (val[0] << 31) | (val[1] & 0xF)
    #     self.write(OPTICAL_SWITCH_REG, val_to_wr)

    
    # @property
    # def inner_trig_enable(self) -> bool:
    #     return (self.read(USE_INNER_TRIG_REG) != 0)
    # @inner_trig_enable.setter
    # def inner_trig_enable(self, val):
    #     val_to_wr = 1 if(val) else 0
    #     self.write(USE_INNER_TRIG_REG, val_to_wr)

    
    # @property
    # def inner_trig_width(self) -> int:
    #     return self.read(INNER_TRIG_WIDTH_REG)
    # @inner_trig_width.setter
    # def inner_trig_width(self, val):
    #     self.write(INNER_TRIG_WIDTH_REG, val)

    
    # @property
    # def inner_trig_cycle(self) -> int:
    #     return self.read(INNER_TRIG_CYCLE_REG)
    # @inner_trig_cycle.setter
    # def inner_trig_cycle(self, val):
    #     self.write(INNER_TRIG_CYCLE_REG, val)

    
    # @property
    # def inner_spec_width(self) -> int:
    #     return self.read(INNER_SPEC_WIDTH_REG)
    # @inner_spec_width.setter
    # def inner_spec_width(self, val):
    #     self.write(INNER_SPEC_WIDTH_REG, val)

    
    # @property
    # def inner_spec_cycle(self) -> int:
    #     return self.read(INNER_SPEC_CYCLE_REG)
    # @inner_spec_cycle.setter
    # def inner_spec_cycle(self, val):
    #     self.write(INNER_SPEC_CYCLE_REG, val)


    # @property
    # def recent_done(self) -> Tuple[int, int]:
    #     raw = self.read(RECENT_DONE_REG)
    #     pingpong_sel = (raw & 0x10) >> 4
    #     os_ch = (raw & 0xF)
    #     return (pingpong_sel, os_ch)
    

    # @property
    # def fpga_version(self) -> int:
    #     return self.read(VERSION_REG)



# class ClockingWizardDriver(DefaultIP):
#     def __init__(self, description):
#         super().__init__(description=description)

#     bindto = ['xilinx.com:ip:clk_wiz:6.0']

#     __CLK0_FREQ_ADDR = 0x208
#     __CLK_LOAD_ADDR = 0x35C
#     __LOCKED_ADDR = 0x4

#     def set_clk0_div(self, ad_sr:AD_SAMPLE_RATE):

#         if ad_sr == AD_SAMPLE_RATE.AD_SR_100MHz:
#             div = 10
#         elif ad_sr == AD_SAMPLE_RATE.AD_SR_200MHz:
#             div = 5
#         else:
#             div = 4

#         self.write(self.__CLK0_FREQ_ADDR, div)
#         self.write(self.__CLK_LOAD_ADDR, 3)
        
#         # Wait until mmcm locked
#         while (self.read(self.__LOCKED_ADDR) == 0):
#             pass



class FPGA_OVERLAY(Overlay):

    def __init__(self, cfg, dtbo=None, download=True, ignore_version=False, device=None):
        self.cfg = cfg
        super().__init__(cfg.bitfile, dtbo=dtbo, download=download, ignore_version=ignore_version, device=device)
        
        self.__clear_cma()
        
        self.sysctrl : SysCtrlDriver = self.sysctrl_zu3_0
        self.single_spec_ctrl : SingleSpecCtrlDriver = self.single_spec_ctrl_0
        self.sysctrl.enable = False

        # self.ad_clk : ClockingWizardDriver = self.clk_wiz_2
        
        # cma0 = allocate((2, 16*1024*1024), 'i2')
        # cma1 = allocate((2, 10*1024*1024), 'i2')
        # self.cma = [cma0, cma1]
        self.res_cma = allocate((2, MAX_BULK_SIZE, MAX_CH, MAX_RES_PER_CH),'u4')   #  2(pingpong)*512KB
        self.thresholds_cma = allocate((MAX_CH, MAX_SPEC_LENGTH), 'u2')    


        self.intr = Interrupt('bulk_write_ctrl_0/irq_out')
          
        self.sysctrl.res0_offset = self.res_cma[0].physical_address
        self.sysctrl.res1_offset = self.res_cma[1].physical_address
        self.sysctrl.threshold_offset = self.thresholds_cma.physical_address

        logger.info(f"ALLCATED RES OFFSET 0 : 0x{self.sysctrl.res0_offset:08X}")
        logger.info(f"ALLCATED RES OFFSET 1 : 0x{self.sysctrl.res1_offset:08X}")
        logger.info(f"ALLCATED THRESHOLD OFFSET : 0x{self.sysctrl.threshold_offset:08X}")

        self.set_sys( 
            spec_length=cfg.spec_length,
            start_spec=float2int_present(BASE_FREQ - self.cfg.start_pos),       
            ind_interval=float2int_present(-self.cfg.scan_step),
            bypass_log=cfg.bypass_log,
            bypass_fir=cfg.bypass_fir,
            ch_sel=cfg.ch_sel,
            bulk_num=cfg.bulk_size,
            use_fake_trig=cfg.use_fake_trig,  
            )
        
        self.thresholds_cma[:][:] = cfg.thresholds
        self.thresholds_cma.flush()


    def __clear_cma(self):
        mem = Xlnk()
        mem.xlnk_reset()
    

    def start_sample(self):
        self.sysctrl.enable = True


    def stop_sample(self):
        self.sysctrl.enable = False

    def get_envtemp(self):
        return self.sysctrl.env_temp    


    def set_sys(self, 
        spec_length=None,
        start_spec=None, 
        ind_interval=None,
        bypass_log=None,
        bypass_fir=None,
        ch_sel=None,
        bulk_num=None,
        use_fake_trig=None 
          ):

        former_ena = self.sysctrl.enable
        
        self.sysctrl.enable = False
        logger.debug("Stop sample before set parameter.")

        if spec_length is not None:
            self.sysctrl.spec_length = spec_length
        if start_spec is not None:
            self.sysctrl.start_spec = start_spec
        if ind_interval is not None:
            self.sysctrl.ind_interval = ind_interval
        if spec_length is not None:
            self.sysctrl.spec_length = spec_length
        if bypass_log is not None:
            self.sysctrl.bypass_log = bypass_log
        if bypass_fir is not None:
            self.sysctrl.bypass_fir = bypass_fir
        if ch_sel is not None:
            self.sysctrl.ch_sel = ch_sel
        if bulk_num is not None:
            self.sysctrl.bulk_num = bulk_num
        if use_fake_trig is not None:
            self.sysctrl.use_fake_trig = use_fake_trig

        if(former_ena):
            self.sysctrl.enable = True
            logger.debug("Restart sample.")


    def clean(self):
        self.stop_sample()


    async def read_spec_oneshot(self, ch, spec_length) -> List[int]:
        try:
            self.single_spec_ctrl.single_spec_ch = ch
            while True:
                await asyncio.sleep(0)
                if self.single_spec_ctrl.single_spec_done == 1:
                    break
            l = []
            for i in range(spec_length):
                l.append(self.single_spec_ctrl.single_spec_data)
                await asyncio.sleep(0)
            logger.debug(f"Readed CH{ch} specture : [{l[0]}, {l[1]}, {l[2]}, ..., {l[-2]}, {l[-1]}], length : {spec_length}")
            self.single_spec_ctrl.single_spec_done = 1
            return l

        except asyncio.CancelledError:
            self.single_spec_ctrl.single_spec_done = 1
            logger.warning("Single spec reading cancelled.")
            return None




class FPGA_WORKER(WORKER):

    handlers = HANDLERS()

    def __init__(self, cfg:config, skt:socket) -> None:
        super().__init__()
        self.cfg = cfg
        self.fpga = FPGA_OVERLAY(cfg, ignore_version=True)
        self._sample_tasks = None
        self._spec_oneshot_task = None
        self.__s = skt
        self.os_ctrl = os_ctrl.OS_CTRL(cfg)
        
        # self.tcp_server = TCP_RESULT_SERVER(cfg)
        self.tasks = []
        # self.tasks = [self.tcp_server.launch_server()]
        self.start_t = time()
        self.counter = 0

        self.os_ctrl.set_start_freq()
        self.os_ctrl.set_stop_freq()
        self.os_ctrl.set_scan_speed()
        self.os_ctrl.set_scan_step()
        self.os_ctrl.set_open(1)
        self.os_ctrl.set_scan_mode()



    async def __sample_data(self, cma, intr):
        self.start_t = time()
        self.counter = 0
        while True:
            await intr.wait()
            elapsed_t = time() - self.start_t
            ch_num = CHANEL_SEL(self.cfg.ch_sel).map_to_ch_num()
            frame_res_len = ch_num * MAX_RES_PER_CH
            frame_bytes_len = frame_res_len * 2
            sel = 1 if (self.fpga.sysctrl.pingpong_sel == 0) else 0

            temp = self.fpga.get_envtemp()

            cali_val = self.__get_temp_cali_value()
            for bulk_i in range(self.cfg.bulk_size):
                self.counter = 0 if self.counter >= 0xFFFFFFFF else (self.counter + 1)
                # header = pack('!4sIBB', b'FITR', frame_bytes_len, self.counter&0xFF, ch_num)

                # # mv = memoryview(cma[sel][bulk_i]).cast('B')
                res = cma[sel][bulk_i]
                res_cali = np.where(res > 0, res + cali_val, 0)
                # mv = memoryview(res_cali).cast('B')
                # self.tcp_server.write_bytes(header)
                # # self.tcp_server.write(res)
                # self.tcp_server.write_view(mv[:frame_bytes_len])

                await self.__send_pack_wl((self.cfg.pc_ip, self.cfg.pc_port), res_cali, temp)
                # await self.__send_pack_wl((self.cfg.pc_ip, self.cfg.pc_port), cma[sel][bulk_i], temp)

            logger.debug(f"BULK NUM: {self.counter}. ELAPSED TIME: {elapsed_t}s. SAMPPLE RATE: {self.counter/elapsed_t}.")


    async def __send_pack_wl(self, addr, data, temp):
        ch_num = CHANEL_SEL(self.cfg.ch_sel).map_to_ch_num()
        pld_len = ch_num * (MAX_VLD_SENSOR * 4 + 2) + 6
        header = pack(f'!BBI', 0x30, 0x02, pld_len)
        # t_pld = pack(f'!H', temp)
        pld = header
        for ch in range(ch_num):
   
            pld += pack(f'!{MAX_VLD_SENSOR}IH', *data[ch][:MAX_VLD_SENSOR], temp)
        
        for p_head in range(0, len(pld), 1472):
            chunk = pld[p_head:p_head+1472]
            self.__s.sendto(chunk, addr)
            await asyncio.sleep(0.0005)




    def __all_sample_done(self) -> bool:
        if self._sample_tasks is None:
            return True
        else:
            return self._sample_tasks.done()
        
    def __get_temp_cali_value(self) -> int:
        temp = self.fpga.get_envtemp()
        logger.debug(f"Temp int value is {temp}.")
        temp_index = temp - TEMP_CAL_START
        # clamp to -10 to 60
        temp_index = 0 if temp_index < 0 else \
                        TEMP_CAL_END if temp_index > TEMP_CAL_END  else \
                        temp_index
        cali_val = self.cfg.temp_calibrate[temp_index]
        return cali_val
            

    # def float2int_present(self, num) -> int:
    #     packed = struct.pack('!f', float(num))
    #     integers = struct.unpack('!I', packed)[0]
    #     return integers



    @handlers(CMD.Type.START_SAMPLE)
    def start_sample(self, cmd):

        self.fpga.intr.waiting = False

        self.fpga.start_sample()

        if self.__all_sample_done():
            self._sample_tasks = asyncio.create_task(self.__sample_data(
                self.fpga.res_cma, self.fpga.intr))
        
        return None


    @handlers(CMD.Type.STOP_SAMPLE)
    def stop_sample(self, cmd):

        self.fpga.stop_sample()

        if not self.__all_sample_done():
            self._sample_tasks.cancel()

        return [1]


    @handlers(CMD.Type.GET_VERSION)
    def get_version(self, cmd):
        lo = self.cfg.software_version & 0xFFFF
        hi = self.cfg.software_version >> 16
        return [hi, lo]     

    @handlers(CMD.Type.GET_SERIAL)
    def get_serial(self, cmd):
        lo = self.cfg.serial & 0xFFFF
        hi = self.cfg.serial >> 16
        return [hi, lo]     

    @handlers(CMD.Type.GET_PARA)        
    def get_para(self, cmd):
        scan_speed = 0x65
        # scan_speed = self.cfg.scan_speed
        ch_num = CHANEL_SEL(self.cfg.ch_sel).map_to_ch_num()
        sensor_num = 30           
        peak_interval = 0x28     # TODO  
        return [scan_speed, ch_num, sensor_num, peak_interval]   

    @handlers(CMD.Type.GET_SCAN_PARA)        
    def get_scan_para(self, cmd):
        return [self.cfg.start_pos, self.cfg.scan_step, self.cfg.stop_pos, 1]
    

    @handlers(CMD.Type.GET_CH_CFG)        
    def get_ch_cfg(self, cmd):
        ret = []
        for i in range(MAX_CH):
            ret += [self.cfg.thresholds[i][0], self.cfg.amp[i]]
        return ret

    
    @handlers(CMD.Type.SET_SCAN_PARA)
    def set_scan_para(self, cmd:CMD_SET_SCAN_PARA):
        self.cfg.start_pos = cmd.start_pos
        self.cfg.stop_pos = cmd.stop_pos
        self.cfg.scan_step = cmd.scan_step
        self.cfg.spec_length = int(math.floor((cmd.stop_pos - cmd.start_pos) / cmd.scan_step)) + 1
        self.fpga.set_sys(  \
                        spec_length=self.cfg.spec_length,    \
                        start_spec=float2int_present(BASE_FREQ - self.cfg.start_pos),       \
                        ind_interval=float2int_present(-self.cfg.scan_step))
        ret_save = self.cfg.save()

        self.os_ctrl.set_start_freq()
        self.os_ctrl.set_scan_step()
        self.os_ctrl.set_stop_freq()

        return [int(ret_save)]


    @handlers(CMD.Type.SET_THRESHOLD)
    def set_threshold(self, cmd:CMD_SET_THRESHOLD):
        self.cfg.thresholds[cmd.ch] = cmd.thresold
        self.fpga.thresholds_cma[:][:] = self.cfg.thresholds
        self.fpga.thresholds_cma.flush()
        return [1]
    
    
    @handlers(CMD.Type.SET_AMPLIFY)             # TODO : 
    def set_amplify(self, cmd:CMD_SET_AMPLIFY):
        self.cfg.amp[cmd.ch] = cmd.amp

        i2c_ch = I2C_ADDR_MAPPING[cmd.ch][0]
        dev_addr = I2C_ADDR_MAPPING[cmd.ch][1]

        i2c_val = AMP_MAPPING[(5 if cmd.amp>5 else cmd.amp)]
        self.__set_i2c(i2c_ch, dev_addr, 1, i2c_val)

        ret_save = self.cfg.save()

        return [1]
    
        
    @handlers(CMD.Type.SET_PEAK_INTERVAL)             # TODO
    def set_peak_interval(self, cmd:CMD_SET_PEAK_INTERVAL):
        return [0]
    
        
    @handlers(CMD.Type.SAVE_THRESHOLD)            
    def save_threshold(self, cmd:CMD_SAVE_THRESHOLD):
        self.cfg.save_threshold()
        return None
    
        
    @handlers(CMD.Type.START_TEST)         #TODO   
    def start_test(self, cmd):
        return None
        
    @handlers(CMD.Type.START_SPEC_SAMPLE)         #TODO   
    def start_spec_sample(self, cmd):
        return None


    @handlers(CMD.Type.GET_ONESHOT_SPEC)
    async def get_oneshot_spec(self, cmd:CMD_GET_ONESHOT_SPEC):
        spec_length = self.cfg.spec_length
        ch = cmd.ch
        spec_list = await self.fpga.read_spec_oneshot(ch, spec_length)
        return [ch] + [self.cfg.amp[ch]] + spec_list
    

    # @handlers(CMD.Type.SET_SPEC_LENGTH)
    # def set_spec_length(self, cmd:CMD_SET_SPEC_LENGTH):
    #     self.cfg.spec_length = cmd.spec_length
    #     self.fpga.set_sys(spec_length=cmd.spec_length)
    #     return True
    

    # @handlers(CMD.Type.SET_CHANNEL_SWITCH)
    # def set_channel_switch(self, cmd:CMD_SET_CHANNEL_SWITCH):
    #     self.cfg.ch_sel = cmd.channel_sel
    #     self.fpga.set_sys(ch_sel=int(cmd.channel_sel))
    #     return True
    
    @handlers(CMD.Type.SET_IIC)            
    def set_i2c(self, cmd:CMD_SET_IIC):
        return [1 if self.__set_i2c(cmd.i2c_ch, cmd.dev_addr, cmd.reg_addr, cmd.val) else 0]


    def __set_i2c(self, i2c_ch, dev_addr, reg_addr, val):     
        axi_iic_dict = {
            0 : self.fpga.axi_iic_0,
            1 : self.fpga.axi_iic_1,
            2 : self.fpga.axi_iic_2,
            3 : self.fpga.axi_iic_3,
        }

        axi_iic = axi_iic_dict[i2c_ch]

        try:
            axi_iic.send(dev_addr, [reg_addr, val], 2, 0)
        except BaseException as e:
            logger.error(e, exc_info=True)
            return False
        return True


    # @handlers(CMD.Type.GET_I2C)
    # def get_i2c(self, cmd:CMD_GET_I2C):     
    #     i2c_ch = I2C_CH_MAPPING[cmd.i2c_ch]
    #     dev_addr = cmd.dev_addr
    #     reg_addr = cmd.reg_addr
    #     axi_iic_dict = {
    #         0 : self.fpga.axi_iic_0,
    #         1 : self.fpga.axi_iic_1,
    #         2 : self.fpga.axi_iic_2,
    #         3 : self.fpga.axi_iic_3,
    #     }

    #     axi_iic = axi_iic_dict[i2c_ch]

    #     try:
    #         rx_data = bytes(1)
    #         axi_iic.send(dev_addr, [reg_addr], 1, 1)
    #         axi_iic.receive(dev_addr, rx_data, len(rx_data), 0)
    #         int_data = unpack("B", rx_data)[0]
    #     except BaseException as e:
    #         logger.error(e, exc_info=True)
    #         return 0xFFFF
    #     return int_data
    

    # @handlers(CMD.Type.SET_IND_INTERVAL)
    # def set_ind_interval(self, cmd:CMD_SET_IND_INTERVAL):
    #     self.cfg.ind_interval = cmd.ind_interval
    #     self.fpga.set_sys(ind_interval=cmd.ind_interval)
    #     return True
    

    # @handlers(CMD.Type.SET_START_SPEC)
    # def set_start_spec(self, cmd:CMD_SET_START_SPEC):
    #     self.cfg.start_spec = cmd.start_spec
    #     self.fpga.set_sys(start_spec=cmd.start_spec)
    #     return True
    

    # @handlers(CMD.Type.SET_BULK_SIZE)
    # def set_bulk_size(self, cmd:CMD_SET_BULK_SIZE):
    #     self.cfg.bulk_size = cmd.bulk_size
    #     self.fpga.set_sys(bulk_num=cmd.bulk_size)
    #     return True


    # @handlers(CMD.Type.SET_THRESHOLD)
    # def set_threshold(self, cmd:CMD_SET_THRESHOLD):
    #     self.cfg.thresholds[cmd.channel] = cmd.threshold
    #     self.fpga.thresholds_cma[:][:] = self.cfg.thresholds
    #     self.fpga.thresholds_cma.flush()
    #     return True
    
    
    # @handlers(CMD.Type.SET_THRESHOLD_PER_POINT)
    # def set_threshold_per_point(self, cmd:CMD_SET_THRESHOLD_PER_POINT):
    #     self.cfg.thresholds[cmd.channel] = cmd.threshold_per_point
    #     self.fpga.thresholds_cma[:][:] = self.cfg.thresholds
    #     self.fpga.thresholds_cma.flush()
    #     return True
    

    # @handlers(CMD.Type.GET_SPECTURE)
    # def get_specture(self, cmd:CMD_GET_SPECTURE):
    #     if(self._spec_oneshot_task is None or self._spec_oneshot_task.done()):
    #         self._spec_oneshot_task = asyncio.create_task(self.__read_spec_oneshot(cmd.channel))
    #     return True
    

    def clean(self):
        self.fpga.clean()

        
